/**
 * 
 */
package ch.uzh.ifi.ddis.ds2011.a3;

import com.signalcollect.javaapi.DataGraphVertex;

/**
 * Author: Floarea Serban<br>
 * The University of Z&uuml;rich<br>
 * Dynamic and Distributed Information Systems Group<br>
 * Date: Nov 28, 2011<br>
 * <br>
 * <p/>
 * serban@ifi.uzh.ch<br>
 * http://www.ifi.uzh.ch/ddis/people/serban/<br>
 * <br>
 */
public class ConvolutionAgent extends
		DataGraphVertex<Integer, Integer, Integer> {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * Calculates the discrete value at x,y of the 2D gaussian distribution.
	 * 
	 * @param theta
	 *            the theta value for the gaussian distribution
	 * @param x
	 *            the point at which to calculate the discrete value
	 * @param y
	 *            the point at which to calculate the discrete value
	 * @return the discrete gaussian value
	 */
	private static double gaussianDiscrete2D(double theta, int x, int y) {
		double g = 0;
		for (double ySubPixel = y - 0.5F; ySubPixel < y + 0.6F; ySubPixel += 0.1F) {
			for (double xSubPixel = x - 0.5F; xSubPixel < x + 0.6F; xSubPixel += 0.1F) {
				g = g
						+ (double) ((1 / (2 * Math.PI * theta * theta)) * Math
								.pow(Math.E,
										-(xSubPixel * xSubPixel + ySubPixel
												* ySubPixel)
												/ (2 * theta * theta)));
			}
		}
		g = g / 121;
		// System.out.println(g);
		return g;
	}

	/**
	 * Calculates several discrete values of the 2D gaussian distribution.
	 * 
	 * @param theta
	 *            the theta value for the gaussian distribution
	 * @param size
	 *            the number of discrete values to calculate (pixels)
	 * @return 2Darray (size*size) containing the calculated discrete values
	 */
	private static double[][] gaussian2D(double theta, int size) {
		double[][] kernel = new double[size][size];
		for (int j = 0; j < size; ++j) {
			for (int i = 0; i < size; ++i) {
				kernel[i][j] = gaussianDiscrete2D(theta, i - (size / 2), j
						- (size / 2));
			}
		}

		return kernel;
	}

	/**
	 * Applies the Gaussian filter.
	 * @param input
	 * @param k
	 * @param kernelWidth
	 * @param kernelHeight
	 * @return
	 */
	public static int applyConvolution(int[][] input, double[][] k,
			int kernelWidth, int kernelHeight) {
		int output = 0;
		for (int i = 0; i < kernelWidth; ++i) {
			for (int j = 0; j < kernelHeight; ++j) {
				output = output + (int) Math.floor(input[i][j] * k[i][j]);
			}
		}
		return output;
	}

	private double[][] gauss;

	private int imgw;
	private int imgh;

	public ConvolutionAgent(Integer id, Integer initialState, int w, int h) {
		super(id, initialState);
		// you can experiment with several values but if possible keep these
		// ones in your final solution
		this.gauss = gaussian2D(0.9, 3);
		this.imgw = w;
		this.imgh = h;
	}

	@Override
	public Integer collect(Integer oldState, Iterable<Integer> mostRecentSignals) {
		// TODO: IMPLEMENT THIS METHOD ! Use the applyConvolutionMethod to apply
		// the Gaussian filter
		return null;
	}
}